//
//  checked_ptr.h
//  Starcraft Replay QL
//
//  Created by Félix Cloutier on 11-11-04.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#ifndef Starcraft_Replay_QL_checked_ptr_h
#define Starcraft_Replay_QL_checked_ptr_h

#include <exception>

class bad_access : public std::exception
{
public:
	virtual const char* what() const throw()
	{
		return "trying to deference past the end of the pointer";
	}
};

template<typename T>
class checked_ptr
{
	T* begin;
	T* end;
	T* current;
	
	void check_apply(T* value)
	{
		check(value, true);
		current = value;
	}
	
	bool check(T* arbitrary, bool throwOnError = false) const
	{
		bool valid = arbitrary >= begin && arbitrary <= end;
		if (!valid && throwOnError)
			throw bad_access();
		return valid;
	}
	
public:
	checked_ptr(T* begin, T* end)
	{
		this->begin = begin;
		this->end = end;
		this->current = begin;
	}
	
	checked_ptr(const checked_ptr<T>& that)
	{
		begin = const_cast<T*>(that.begin);
		end = const_cast<T*>(that.end);
		current = const_cast<T*>(that.current);
	}
	
	T& operator*()
	{
		if (current == end || current + sizeof(T) > end)
			throw bad_access();
		return *current;
	}
	
	T* operator->()
	{
		return this->operator*();
	}
	
	const T& operator*() const
	{
		return current;
	}
	
	T& operator++()
	{
		check_apply(current + 1);
		return *current;
	}
	
	T& operator--()
	{
		check_apply(current - 1);
		return current;
	}
	
	T& operator++(int)
	{
		T* ptr = current;
		this->operator++();
		return *ptr;
	}
	
	T& operator--(int)
	{
		T* ptr = current;
		this->operator--();
		return *ptr;
	}
	
	T& operator[](size_t index)
	{
		check(current + index, true);
		return current[index];
	}
	
	const T& operator[](size_t index) const
	{
		check(current + index, true);
		return current[index];
	}
	
	checked_ptr<T>& operator+=(size_t operand)
	{
		check_apply(current + operand);
		return *this;
	}
	
	checked_ptr<T>& operator-=(size_t operand)
	{
		check_apply(current - operand);
		return *this;
	}
	
	bool check(size_t index) const
	{
		return check(current + index, true);
	}
	
	bool ended() const
	{
		return current == end;
	}
	
	bool operator==(const checked_ptr<T>& that) const
	{
		return that.current == current;
	}
	
	bool operator!=(const checked_ptr<T>& that) const
	{
		return that.current != current;
	}
	
	bool operator<(const checked_ptr<T>& that) const
	{
		return current < that.current;
	}
	
	bool operator>(const checked_ptr<T>& that) const
	{
		return current > that.current;
	}
	
	bool operator>=(const checked_ptr<T>& that) const
	{
		return current >= that.current;
	}
	
	bool operator<=(const checked_ptr<T>& that) const
	{
		return current >= that.current;
	}
	
	T* to_unchecked()
	{
		return current;
	}
	
	const T* to_unchecked() const
	{
		return current;
	}
};

template<typename T>
checked_ptr<T> operator+(const checked_ptr<T>& a, size_t operand)
{
	checked_ptr<T> result = a;
	result += operand;
	return result;
}

template<typename T>
checked_ptr<T> operator-(const checked_ptr<T>& a, size_t operand)
{
	checked_ptr<T> result = a;
	result -= operand;
	return result;
}

#endif
